// game.cpp
//

#include "game.hpp"

#include "glm/ext/matrix_transform.hpp"



// Game::Game
//
Game::Game ()
{
}



// get_camera
//
NDataCamera *Game::get_camera ( int n )
{
  switch(n)
    {
    case 0: return &d_camera_0;
    case 1: return &d_camera_1;
    default:
      GLT_FATAL("wrong camera: %d", n);
      return nullptr;
    }
}



// Game::init
//
void Game::init ()
{
  GLT_TRACE("Game.init()");

  init_models();
  init_cameras();
  init_lights();
  init_map();

  // normals test
  normals_target.model.mesh.create_normals(mod_normals_test.mesh, .05f);
  d_normals_test.set_visible(false);
}



// Game::update
//
void Game::update ( double dtime )
{
  static double game_time = 0.;
  game_time += dtime;

  update_lights(game_time, dtime);
  update_cameras(game_time, dtime);
  update_entities(game_time, dtime);

  // normals test
  d_normals_test.transfo = normals_target.transfo;
}



// Game::init_map
//
void Game::init_map ()
{
  ZMap zmap{map.get_width() + 1, map.get_height() + 1};
  zmap.randomize(10.f);
  zmap.smooth(3);
  zmap.level(-5.f);
  map.apply_zmap(zmap);
  map.update_normals();
  map.smooth_normals();
  map.create_mesh(mod_map.mesh);
  map.create_grid_mesh(mod_map_grid.mesh, {1.f, 1.f, 0.f});
  mod_map.mesh.create_normals(mod_map_normals.mesh, .5f);
                                 
  glm::vec3 map_pos{
    float(map.get_width()) * -.5f,
    0.f,
    float(map.get_height()) * -.5f,
  };

  d_map.transfo.set_position(map_pos);
  d_map_grid.transfo.set_position(map_pos);
  d_map_normals.transfo.set_position(map_pos);

  d_map.set_visible(true);
  d_map_grid.set_visible(false);
  d_map_normals.set_visible(false);
}



// Game::init_models
//
void Game::init_models ()
{
  mod_axis.mesh.axis();
  mod_axis.mesh.scale(3.f);

  mod_camera.mesh.load(datadir + "/models/camera.obj");
  mod_camera.mesh.scale(.2f);
  
  mod_cube.mesh.load(datadir + "/models/cube.obj");
  mod_cube.mesh.scale(.5f);
  mod_cube.mesh.uv_scale(4.f);

  mod_sphere.mesh.load(datadir + "/models/sphere.obj");
  mod_sphere.mesh.scale(.5f);
  mod_sphere.mesh.uv_scale(4.f);
  
  mod_suzanne.mesh.load(datadir + "/models/suzanne.obj");
  mod_suzanne.mesh.scale(.5f);
  mod_suzanne.mesh.uv_scale(4.f);
}



// Game::init_lights
//
void Game::init_lights ()
{
  static float light_obj_scale = .1f;

  // light 1
  d_light_1.wgl_light.set_type(wgl::LightType::Directional);
  d_light_1.wgl_light.set_color({.9f, 1.f, .8f});  
  d_light_1.wgl_light.set_levels(.5f, 1.f, .1f);

  // light 2
  d_light_2.wgl_light.set_type(wgl::LightType::Directional);
  d_light_2.wgl_light.set_color({1.f, 1.f, 1.f});  
  d_light_2.wgl_light.set_levels(.5f, 1.f, .1f);

  // models
  d_light_obj_1.transfo.set_scale(light_obj_scale);
  d_light_obj_1.set_visible(d_light_1.wgl_light.get_type() != wgl::LightType::None);

  d_light_obj_2.transfo.set_scale(light_obj_scale);
  d_light_obj_2.set_visible(d_light_2.wgl_light.get_type() != wgl::LightType::None);
}



// star_light
//
static void star_light ( double hour,
                         double start_hour,
                         double end_hour,
                         glm::vec3 &pos,
                         float &strength )
{
  if (start_hour > end_hour)
    {
      if (hour < end_hour)
        hour += 1.f;
      end_hour += 1.f;
    }
  
  if (hour >= start_hour && hour <= end_hour)
    {
      float angle = std::numbers::pi * ((hour - start_hour) / (end_hour - start_hour));
      pos = 
        {
          std::cos(angle),
          std::sin(angle),
          0.f,
        };
      strength = glm::dot(pos, glm::vec3{0.f, 1.f, 0.f});
    }
  else
    {
      pos = glm::vec3{0.f};
      strength = 0.f;
    }
}



// Game::init_cameras
//
void Game::init_cameras ()
{
  wgl::Camera &c0 = d_camera_0.wgl_camera;
  wgl::Camera &c1 = d_camera_1.wgl_camera;

  c0.set_position({0.f, 5.f, 10.f});
  // c0.set_rotation
  //   ({
  //     glm::radians(-30.f),
  //     0.f,
  //     game_time,
  //   });

  c1.set_position({-10.f, 5.f, 10.f});
  c1.set_rotation
    ({
      0.f,// glm::radians(-90.f),
      0.f,
      glm::radians(-90.f),
    });
}



// Game::update_cameras
//
void Game::update_cameras ( double game_time,
                            double dtime )
{
  // GLM_FUNC_QUALIFIER mat<4, 4, T, Q> orientate4
  // (
  // 	vec<3, T, Q> const& angles
  // )
  // {
  // 	return yawPitchRoll(angles.z, angles.x, angles.y);
  // }

  // wgl::Camera &c0 = d_camera_0.wgl_camera;
  wgl::Camera &c1 = d_camera_1.wgl_camera;

  // c0.set_position({0.f, 5.f, 10.f});
  // c0.set_rotation
  //   ({
  //     glm::radians(-30.f),
  //     0.f,
  //     game_time,
  //   });

  // c1.set_position({-10.f, 5.f, 10.f});
  c1.set_rotation
    ({
      0.f,// glm::radians(-90.f),
      0.f,
      glm::radians(-90.f),
    });
  
  d_cam_marker.set_visible(true);
  d_cam_marker.transfo.set_position(d_camera_0.wgl_camera.get_position());
  d_cam_marker.transfo.set_rotation(d_camera_0.wgl_camera.get_rotation());
  d_cam_marker.transfo.set_scale(5.f);
}



// Game::update_lights
//
void Game::update_lights ( double game_time,
                           double dtime )
{
  static constexpr float day_cycle = .1f;
  
  static constexpr float day_start = .15f;
  static constexpr float day_end = .85f;
  static constexpr float sun_dist = 5.f;
  static constexpr float sun_ambient = .5f;
  static constexpr float sun_diffuse = 1.f;
  static constexpr float sun_specular = .1f;

  static constexpr float night_start = .65f;
  static constexpr float night_end = .35f;
  static constexpr float moon_dist = 5.f;
  static constexpr float moon_ambient = .25f;
  static constexpr float moon_diffuse = .25f;
  static constexpr float moon_specular = .05f;
  
  float day, hour;
  hour = std::modf(game_time * day_cycle, &day);

  glm::vec3 sun_pos;
  float sun_strength;
  star_light(hour, day_start, day_end, sun_pos, sun_strength);

  glm::vec3 moon_pos;
  float moon_strength;
  star_light(hour, night_start, night_end, moon_pos, moon_strength);
  
  d_light_1.wgl_light.set_position(sun_pos * sun_dist);
  d_light_1.wgl_light.set_levels(sun_ambient * sun_strength,
                                 sun_diffuse * sun_strength,
                                 sun_specular * sun_strength);
  
  d_light_2.wgl_light.set_position(moon_pos * moon_dist);
  d_light_2.wgl_light.set_levels(moon_ambient * moon_strength,
                                 moon_diffuse * moon_strength,
                                 moon_specular * moon_strength);
  
  // light objs tracking
  d_light_obj_1.transfo.set_position(d_light_1.wgl_light.get_position());
  d_light_obj_2.transfo.set_position(d_light_2.wgl_light.get_position());
}



// Game::update_entities
//
void Game::update_entities ( double game_time,
                             double dtime )
{
  static float rot_speed = 2.f;
  
  d_obj_1.transfo.set_position({0.f, 0.f, -1.5f});

  d_obj_2.transfo.set_position
    ({
      0.f,
      std::sin(game_time * 3.f) + 5.f,
      0.f
    });
  d_obj_2.transfo.set_rotation
    ({
      game_time * rot_speed,
      game_time * rot_speed * 1.3f,
      game_time * rot_speed * 1.7f,
    });

  d_obj_3.transfo.set_position({0.f, 0.f, 1.5f});
}
